#include "myopenglwidget.h"
#include<QTime>
#include<QElapsedTimer>

QElapsedTimer elapseTimer;
unsigned int VBO,VAO,EBO;
float fov=45;
QVector3D cameraPos(0.0f,0.0f,3.0f);
QVector3D cameraFront(0.0f,0.0f,-1.0f);

QVector3D lightColor(0.0f,1.0f,0.0f);
QVector3D lightPos(4.0f,4.0f,-1.0f);
QVector3D viewPos(3.0f,3.0f,5.0f);
QVector3D objectColor(1.0f,0.5f,0.31f);
QMatrix4x4 projection,view,model;
float vertices[]={
    -0.5f,-0.5f,-0.5f,0.0f,0.0f,-1.0f,
    0.5f,-0.5f,-0.5f,0.0f,0.0f,-1.0f,
    0.5f,0.5f,-0.5f,0.0f,0.0f,-1.0f,
    0.5f,0.5f,-0.5f,0.0f,0.0f,-1.0f,
    -0.5f,0.5f,-0.5f,0.0f,0.0f,-1.0f,
    -0.5f,-0.5f,-0.5f,0.0f,0.0f,-1.0f,

    -0.5f,-0.5f,0.5f,0.0f,0.0f,1.0f,
    0.5f,-0.5f,0.5f,0.0f,0.0f,1.0f,
    0.5f,0.5f,0.5f,0.0f,0.0f,1.0f,
    0.5f,0.5f,0.5f,0.0f,0.0f,1.0f,
    -0.5f,0.5f,0.5f,0.0f,0.0f,1.0f,
    -0.5f,-0.5f,0.5f,0.0f,0.0f,1.0f,

    -0.5f,0.5f,0.5f,-1.0f,0.0f,0.0f,
    -0.5f,0.5f,-0.5f,-1.0f,0.0f,0.0f,
    -0.5f,-0.5f,-0.5f,-1.0f,0.0f,0.0f,
    -0.5f,-0.5f,-0.5f,-1.0f,0.0f,0.0f,
    -0.5f,-0.5f,0.5f,-1.0f,0.0f,0.0f,
    -0.5f,0.5f,0.5f,-1.0f,0.0f,0.0f,

    0.5f,0.5f,0.5f,1.0f,0.0f,0.0f,
    0.5f,0.5f,-0.5f,1.0f,0.0f,0.0f,
    0.5f,-0.5f,-0.5f,1.0f,0.0f,0.0f,
    0.5f,-0.5f,-0.5f,1.0f,0.0f,0.0f,
    0.5f,-0.5f,0.5f,1.0f,0.0f,0.0f,
    0.5f,0.5f,0.5f,1.0f,0.0f,0.0f,

    -0.5f,-0.5f,-0.5f,0.0f,-1.0f,0.0f,
    0.5f,-0.5f,-0.5f,0.0f,-1.0f,0.0f,
    0.5f,-0.5f,0.5f,0.0f,-1.0f,0.0f,
    0.5f,-0.5f,0.5f,0.0f,-1.0f,0.0f,
    -0.5f,-0.5f,0.5f,0.0f,-1.0f,0.0f,
    -0.5f,-0.5f,-0.5f,0.0f,-1.0f,0.0f,

    -0.5f,0.5f,-0.5f,0.0f,1.0f,0.0f,
    0.5f,0.5f,-0.5f,0.0f,1.0f,0.0f,
    0.5f,0.5f,0.5f,0.0f,1.0f,0.0f,
    0.5f,0.5f,0.5f,0.0f,1.0f,0.0f,
    -0.5f,0.5f,0.5f,0.0f,1.0f,0.0f,
    -0.5f,0.5f,-0.5f,0.0f,1.0f,0.0f,

};

MyOpenglWidget::MyOpenglWidget(QWidget *parent):QOpenGLWidget(parent) {
    setFocusPolicy(Qt::StrongFocus);
    timer.setInterval(200);
    connect(&timer,&QTimer::timeout,[this](){
        update();
    });

    timer.start();
    elapseTimer.start();
}

MyOpenglWidget::~MyOpenglWidget()
{
    delete textureWall;
    makeCurrent();
    glDeleteVertexArrays(1,&VAO);
    glDeleteBuffers(1,&VBO);
    glDeleteBuffers(1,&EBO);

    doneCurrent();
}

void MyOpenglWidget::drawRectangle()
{
    ShapeType=GL_TRIANGLES;
    isDraw=true;
    update();
}

void MyOpenglWidget::clear()
{
    isDraw=false;
    update();
}

void MyOpenglWidget::setPolygonMode(bool isWire)
{
    makeCurrent();
    if(isWire)
        glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
    else
        glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
    doneCurrent();
    update();
}

void MyOpenglWidget::initializeGL()
{
    this->initializeOpenGLFunctions();
     glEnable(GL_DEPTH_TEST);
    glGenVertexArrays(1,&VAO);
    glGenBuffers(1,&VBO);
    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER,VBO);

    //将数据传入显存
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);

    //告知显卡如何解析
    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,6*sizeof(float),(GLvoid*)0);
    glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,6*sizeof(float),(GLvoid*)(3*sizeof(float)));

    //开启VAO的属性
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    //glEnableVertexAttribArray(2);

    //EBO
    //glGenBuffers(1,&EBO);
    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);
    //glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);

    //lightingShaders
    lightingShaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/lighting.vert");
    lightingShaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/lighting.frag");
    bool lightsuccess=lightingShaderProgram.link();
    if(!lightsuccess)
        qDebug()<<lightingShaderProgram.log();
    lightingShaderProgram.bind();
    lightingShaderProgram.setUniformValue("lightColor",lightColor);

    //shaders
    shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/shapes.vert");
    shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/shapes.frag");

    bool success=shaderProgram.link();
    if(!success)
        qDebug()<<shaderProgram.log();
    shaderProgram.bind();

    textureWall=new QOpenGLTexture(QImage(":/resources/container.jpg").mirrored());
    textureContainer=new QOpenGLTexture(QImage(":/resources/awesomeface.png").mirrored());

    textureContainer->generateMipMaps();
    shaderProgram.setUniformValue("textureWall",0);
    shaderProgram.setUniformValue("textureContainer",1);
    //解绑
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER,0);

    projection.perspective(fov,(float)width()/height(),0.1f,100.0f);
    shaderProgram.setUniformValue("projection",projection);
}

void MyOpenglWidget::resizeGL(int w, int h)
{

}

void MyOpenglWidget::paintGL()
{
    glClearColor(0.2f,0.3f,0.3f,1.0f);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    if(!isDraw)
        return;

    shaderProgram.bind();
    glBindVertexArray(VAO);

    shaderProgram.setUniformValue("viewPos",viewPos);
    shaderProgram.setUniformValue("lightPos",lightPos);
    shaderProgram.setUniformValue("objectColor",objectColor);
    shaderProgram.setUniformValue("lightColor",lightColor);
    float timeValue=elapseTimer.elapsed();


    view.setToIdentity();
    view.lookAt(cameraPos,cameraPos+cameraFront,QVector3D(0.0f,1.0f,0.0f));


    shaderProgram.setUniformValue("view",view);

    model.setToIdentity();
    model.rotate(timeValue/10,1.0f,0.3f,0.5f);
    shaderProgram.setUniformValue("model",model);
    //glDrawElements(ShapeType,6,GL_UNSIGNED_INT,0);

    glDrawArrays(ShapeType,0,36);

    lightingShaderProgram.bind();
    model.setToIdentity();
    model.translate(lightPos);
    model.scale(0.25);
    lightingShaderProgram.setUniformValue("model",model);
    lightingShaderProgram.setUniformValue("view",view);
    lightingShaderProgram.setUniformValue("projection",projection);
    glDrawArrays(ShapeType,0,36);

    //解绑
    glBindVertexArray(0);
}

float deltaTime;
float lastFrame;
void MyOpenglWidget::keyPressEvent(QKeyEvent *event)
{
    float currentFrame=elapseTimer.elapsed();
    deltaTime=currentFrame-lastFrame;
    if(deltaTime>50)
        deltaTime=50;
    lastFrame=currentFrame;
    float cameraSpeed=deltaTime*0.002;

    QVector3D up(0.0f,1.0f,0.0f);
    QVector3D cameraRight=QVector3D::crossProduct(cameraFront,up);
    switch (event->key()) {
    case Qt::Key_W:
        cameraPos+=cameraSpeed*cameraFront;
        break;
    case Qt::Key_S:
        cameraPos-=cameraSpeed*cameraFront;
        break;
    case Qt::Key_A:
        cameraPos-=cameraSpeed*cameraRight;
        break;
    case Qt::Key_D:
        cameraPos+=cameraSpeed*cameraRight;
        break;
    default:
        break;
    }
    makeCurrent();
    shaderProgram.bind();
    update();
    doneCurrent();
}

void MyOpenglWidget::mouseMoveEvent(QMouseEvent *event)
{
    static QPoint deltaPos;
    static QPoint lastPos;
    static float yaw=-90;
    static float pitch=0;
    auto currentPos=event->pos();
    deltaPos=currentPos-lastPos;

    if(abs(deltaPos.x())>50 || abs(deltaPos.y())>50)
        deltaPos=QPoint(0,0);

    lastPos=currentPos;
    float sensitivity=0.1f;
    deltaPos*=sensitivity;
    yaw+=deltaPos.x();
    pitch-=deltaPos.y();

    if(pitch>89.0f)
        pitch=89.0f;
    if(pitch<-89.0f)
        pitch=-89.0f;

    cameraFront.setX(cos(yaw*M_PI/180)*cos(pitch*M_PI/180));
    cameraFront.setY(sin(pitch*M_PI/180));
    cameraFront.setZ(sin(yaw*M_PI/180)*cos(pitch*M_PI/180));
    cameraFront.normalize();
    update();

}

void MyOpenglWidget::wheelEvent(QWheelEvent *event)
{
    fov-=event->angleDelta().y()/120;
    if(fov<=1.0f)
        fov=1.0f;
    if(fov>=75.0f)
        fov=75.0f;
    makeCurrent();
    projection.setToIdentity();
    projection.perspective(fov,(float)width()/height(),0.1f,100.0f);

    shaderProgram.bind();
    shaderProgram.setUniformValue("projection",projection);

    doneCurrent();
    update();
}

